
#include "bloque.h"


Bloque::Bloque(unsigned int num_bloque, unsigned int size){
	this->num_bloque = num_bloque;
	this->size = size;
	this->libre = size;
	this->datos = new char[this->size];
	for (unsigned int cont = 0; cont < this->size; cont++)
				this->datos[cont] = 0;
	this->set_esp_libre_en_bloque(size - sizeof(unsigned int));
}

Bloque::~Bloque(){
	delete[](this->datos);	
}

unsigned int Bloque::append (TipoStruct* dato){
	unsigned int tam_s_dato = 0; //Tam del dato serializado
	if (this->libre > 0) {
		//Calculo la pos final del dato
		unsigned int pos = this->size - this->libre;
		char* s_dato; //Dato serializado
		dato->serializar(&s_dato, tam_s_dato);
		//Si el dato serializado entra
		if ((tam_s_dato <= this->libre)) {
			//Meto el dato serializado en el bloque
			memcpy(this->datos + pos,s_dato,tam_s_dato);
			//Actualizo el espacio libre del bloque
			this->set_esp_libre_en_bloque(this->libre - tam_s_dato);
		} else
			tam_s_dato = 0;
		//Borro el dato seriaizado
		delete[](s_dato);
	}
	return tam_s_dato;
}

void Bloque::append(char* data, unsigned int tam){
	if ((tam <= this->libre)) {
		//Calculo la pos final del dato
		unsigned int pos = this->size - this->libre;
		//Meto los datos en el bloque al final
		memcpy(this->datos + pos,data,tam);
		//Actualizo el espacio libre del bloque
		this->set_esp_libre_en_bloque(this->libre - tam);		
	}
}

bool Bloque::get_registro (TipoStruct* dato, unsigned int pos){
	pos += sizeof(unsigned int);
	unsigned int size_dato = sizeof(unsigned int);
	//Chequeo si puedo leer el tamanio del registro
	if (((pos + size_dato) <= (this->size - this->libre)) && (dato != NULL)){
		//leo le tamano total que va a tener el contenido
		unsigned int tam_reg;
		memcpy(&tam_reg, this->datos + pos, size_dato);
		//Chequeo si puedo leer todo el registro
		if ((pos + tam_reg) <= (this->size - this->libre)){
			//Deserializo el dato para devolverlo
			dato->deserializar(this->datos +pos,tam_reg);
		} else
			return false;
	} else
		return false;
	return true; 
}

void Bloque::get_registros_desde (TipoStruct* reg_base, std::vector<TipoStruct*> &registros, unsigned int pos){
	pos += sizeof(unsigned int);
	TipoStruct* reg_aux;
	registros.clear();
	while (pos < (this->size - this->libre)) {
		reg_aux = (TipoStruct*)reg_base->clonar();
		get_registro(reg_aux, pos - sizeof(unsigned int));
		registros.push_back(reg_aux);
		pos += get_tamanio_serializado(reg_aux);
	}
}

bool Bloque::escribir_registro(TipoStruct * dato, unsigned int pos){
	char * serializado;
	unsigned int tam;
	
	dato->serializar(&serializado,tam);
	
	bool ret = escribir( serializado, pos, tam);
	delete[] serializado;
	
	return ret;
}

bool Bloque::escribir (const char* buf, unsigned int pos, unsigned int cant_bytes){
	pos += sizeof(unsigned int);
	if(cant_bytes == 0)
		return false;
	//Si lo que voy a escribir entra en el bloque
	if (((pos + cant_bytes) <= this->size) && (buf != NULL)){
		memcpy(this->datos + pos, buf, cant_bytes);
		//Si escribi en un lugar que estaba dentro del espacio libre, actualizo el mismo
		if ( (pos + cant_bytes) > (this->size - this->libre) )
			this->set_esp_libre_en_bloque(this->size - pos - cant_bytes);
		return true;
	} else
		return false;
}

bool Bloque::leer (char* buf, unsigned int pos, unsigned int cant_bytes ){
	pos += sizeof(unsigned int);
	if(cant_bytes == 0)
		return false;
	//Si cae dentro del area de datos
	if (((pos + cant_bytes) <= (this->size - this->libre)) && (buf != NULL)){
		memcpy(buf, this->datos + pos, cant_bytes);
		return true;
	} else
		return false;
}

bool Bloque::comprimir (unsigned int pos, unsigned int cant_bytes){
	pos += sizeof(unsigned int);
	//Si cae dentro del area de datos
	if ((pos + cant_bytes) <= (this->size - this->libre)) {
			//Comprimo el bloque
			for (unsigned int i=pos; i< ( this->size - cant_bytes); i++)
				this->datos[i] = this->datos[cant_bytes+i];
			//Relleno lo que queda con 0
			for (unsigned int i=(this->size-1);i >= (this->size - cant_bytes);i--)
				this->datos[i] = 0;
			//Actualizo el espacio libre
			this->set_esp_libre_en_bloque(this->libre + cant_bytes);
			return true;
	} else
		return false;
}

bool Bloque::expandir (unsigned int pos, unsigned int cant_bytes){
	pos += sizeof(unsigned int);
	//Si cae dentro del area de datos
	//if ((pos + cant_bytes) <= (this->size - this->libre)){
	if (pos <= this->size - this->libre)
	    //Si tengo espacio para expandir lo que deseo
		if (cant_bytes <= this->get_espacio_libre()) {
			//Expando el bloque, copiando lo necesario para hacer espacio y pongo un 0 en el nuevo lugar		
			for( unsigned int i = (this->size - this->libre - 1 ) ; i >= pos; i--)
				this->datos[i + cant_bytes] = datos[i];
			for (unsigned int i = pos; i < (pos + cant_bytes); i++) 
				this->datos[i] = 0;	
			
			//Actualizo el espacio libre
			this->set_esp_libre_en_bloque(this->libre - cant_bytes);
			return true;
		} else
			return false;
	else
		return false;
}

void Bloque::vaciar(){
	if (!esta_vacio()) {
		unsigned int tam = get_tope();
		this->comprimir(0, tam);
	}
}

bool Bloque::esta_vacio(){
	return (this->size == (this->libre + sizeof(unsigned int)));	
}
		
void Bloque::set_num_bloque(unsigned int n_bloque){
	this->num_bloque = n_bloque;
}

unsigned int Bloque::get_num_bloque(){
	return this->num_bloque;	
}

unsigned int Bloque::get_size(){
	return this->size;	
}

unsigned int Bloque::get_tope(){
	return (this->size - this->libre - 4);
}
	
unsigned int Bloque::get_espacio_libre(){
	return this->libre;	
}

unsigned int Bloque::get_esp_ocupado(){
	return (this->get_size() - this->get_espacio_libre());	
}

bool Bloque::buscar_primero_desde(unsigned int pos, TipoStruct* reg, std::vector<ParesDatos*> &consultas, unsigned int &pos_res, unsigned int &size_res){
	pos += sizeof(unsigned int);
	unsigned int pos_act = pos;
	unsigned int tope = this->size - this->libre;
	unsigned int tam = 0;
	bool encontrado = false;
	bool error = false;
	pos_res = 0;
	size_res = 0;
	TipoStruct* reg_aux = NULL;
	while ((pos_act < tope) && (!encontrado) && (!error)) {
		//Obtengo el tam del registro q voy a leer
		tam = *((unsigned int*) (this->datos + pos_act));
		//Si pos_act + tam se me va de largo del bloque marco error
		if ((pos_act + tam) > tope )
			error = true;
		else {
			reg_aux = (TipoStruct*)reg->clonar();
			//Deserializo el registro
			reg_aux->deserializar(this->datos + pos_act, tam);
			//Si cumple condiciones, marco como encontrado, y lleno los datos
			if (cumple_condiciones(*reg_aux, consultas) ) {
				encontrado = true;
				pos_res = pos_act - sizeof(unsigned int);
				size_res = tam;
				*reg = *((TipoDato*)reg_aux);
			} else {
				delete (reg_aux); //Sino lo borro
				reg_aux = NULL;	
			}
		}
		pos_act += tam;
	}
	
	if (reg_aux != NULL) delete (reg_aux);
	
	return encontrado;
}

bool Bloque::buscar_por_clave_desde(unsigned int pos, TipoDato* clave, TipoStruct* reg, unsigned int &pos_res, unsigned int &size_res){
	bool encontrado;
	
	//Armo el vector de la consulta que quiero hacer
	std::vector<ParesDatos*> consulta;
	TipoDato* dato_consulta = clave->clonar();
	ParesDatos * par = new ParesDatos(0, dato_consulta);
	consulta.push_back( par ); 
	
	//Busco el primero q cumpla
	encontrado = buscar_primero_desde(pos, reg, consulta, pos_res, size_res);
	
	//Borro la consulta
	delete (par);
	consulta.clear();
	
	return encontrado;
}

void Bloque::consultar_desde(unsigned int pos, TipoStruct* reg_base, std::vector<ParesDatos*> &consultas, std::vector<TipoStruct*> &resultado){
	resultado.clear();
	unsigned int pos_res;
	unsigned int size_res;
	unsigned int pos_act = pos;
	TipoStruct* reg = (TipoStruct*)reg_base->clonar();
	bool encontrado = buscar_primero_desde(pos_act, reg, consultas, pos_res, size_res);
	if (!encontrado) delete reg;
	while (encontrado) {
		resultado.push_back(reg);
		pos_act = pos_res + size_res;
		reg = (TipoStruct*)reg_base->clonar();
		encontrado = buscar_primero_desde(pos_act, reg, consultas, pos_res, size_res);
		if (!encontrado) delete reg;
	}
	
}



void Bloque::quitar_desde(unsigned int pos, TipoStruct* reg_base, std::vector<ParesDatos*> &consultas, std::vector<TipoStruct*> &quitados, unsigned int &tam_quitado){
	unsigned int pos_res;
	unsigned int size_res;
	unsigned int pos_act = pos;
	tam_quitado = 0;
	TipoStruct* reg = (TipoStruct*)reg_base->clonar();
	bool encontrado = buscar_primero_desde(pos_act, reg, consultas, pos_res, size_res);
	if (encontrado) quitados.push_back(reg);
	else delete(reg);
	while (encontrado) {
		tam_quitado += size_res;
		this->comprimir(pos_res, size_res);
		pos_act = pos_res;
		reg = (TipoStruct*)reg_base->clonar();
		encontrado = buscar_primero_desde(pos_act, reg, consultas, pos_res, size_res);
		if (encontrado) quitados.push_back(reg);
		else delete(reg);
	}
}


char* Bloque::get_contenido(){
	char* result = new char[this->size];
	memcpy(result, this->datos, this->size);
	return result;
}

void Bloque::set_contenido(char* buf, unsigned int tam_buf){
	if (tam_buf == this->size) {
		memcpy(this->datos, buf, tam_buf);
		this->libre = this->get_esp_libre_en_bloque();
	}
}
		
/////////    METODOS PRIVADOS INTERNOS    ///////////

bool Bloque::cumple_condiciones(TipoStruct &reg, std::vector<ParesDatos*> &consultas){
	unsigned int posicion = 0;
	unsigned int num_campo;
	TipoDato * valor;
	bool ok = true;
	
	//Mientras se vayan cumpliendo las condiciones y haya consultas
	while (ok && (posicion<consultas.size()) ){
		//Obtengo el numero de campo
		num_campo = consultas[posicion]->get_numero_campo();
		//Obtengo el valor de la consulta
		valor = consultas[posicion]->get_valor();
		//Si lo que estoy comparando son colecciones
		if(valor->get_codigo_tipo() == COLECCION){
			//Casteo a coleccion
			TipoColec* colec = (TipoColec*) (&reg[num_campo]); 
			ok = false;
			unsigned int i = 0;
			//Busco cada dato en la coleccion, y con q uno esta ya cumple la condicion
			while((!ok)&& (i< valor->get_tamanio())){
				ok = colec->buscar_dato((*((TipoColec*)valor))[i]);
				i++;
			}
		}
		else	//Sino verifico igualdad
			ok = (reg[num_campo] == (*valor) );
		
		posicion++;
	}
	return ok; //Devuelvo el resultado
}

void Bloque::set_esp_libre_en_bloque (unsigned int esp){
	this->libre = esp;
	//Escribo el bloque en la pos 0 con el esp libre
	memcpy(this->datos, &this->libre, sizeof(unsigned int));
}

unsigned int Bloque::get_esp_libre_en_bloque (){
	unsigned int result = 0;
	//Obtengo el esp libre desde la pos 0
	memcpy(&result, this->datos, sizeof(unsigned int));
	return result;
}

unsigned int Bloque::get_tamanio_serializado(TipoStruct* reg){
	unsigned int tam_s_dato;
	char* s_dato; //Dato serializado
	//Serializo el dato y devuelvo el tamanio obtenido
	reg->serializar(&s_dato, tam_s_dato);
	delete[] (s_dato);
	return tam_s_dato;
}
